package cn.ztr.ymcc.service.impl;

import cn.ztr.ymcc.constance.cons;
import cn.ztr.ymcc.domain.KillActivity;
import cn.ztr.ymcc.domain.KillCourse;
import cn.ztr.ymcc.dto.KillDto;
import cn.ztr.ymcc.dto.KillOrderDto;
import cn.ztr.ymcc.mapper.KillActivityMapper;
import cn.ztr.ymcc.mapper.KillCourseMapper;
import cn.ztr.ymcc.result.JsonResult;
import cn.ztr.ymcc.service.IKillActivityService;
import cn.ztr.ymcc.util.AssertUtil;
import cn.ztr.ymcc.util.CodeGenerateUtils;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author ztr
 * @since 2023-03-14
 */
@Service
public class KillActivityServiceImpl extends ServiceImpl<KillActivityMapper, KillActivity> implements IKillActivityService {

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private KillCourseMapper killCourseMapper;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    /**
     * 预热秒杀课程到redis
     */
    @Override
    public boolean addCourse2Redis() {
        Date date = new Date();
        //1 查询需要预热的秒杀
        List<KillActivity> killActivities = baseMapper.selectList(new QueryWrapper<KillActivity>()
                .ge("end_time", date)
                .lt("begin_time", date)
                .eq("publish_status", 0));

        //2 查询活动中的课程
        if(killActivities==null||killActivities.isEmpty())
            return false;
        for (KillActivity killActivity : killActivities) {
            List<KillCourse> killCourses = killCourseMapper.selectList(new QueryWrapper<KillCourse>()
                                            .eq("activity_id", killActivity.getId()));
            HashMap<String,KillCourse> hashMap = new HashMap<>();
            //3 添加秒杀课程库存
            for (KillCourse killCourse : killCourses) {
                //添加验证码
                killCourse.setCode(UUID.randomUUID().toString());
                redisTemplate.opsForValue().set(cons.KILL_STORE+killCourse.getCourseId(),killCourse.getKillCount());

                //4 添加秒杀课程信息
                hashMap.put(killCourse.getCourseId().toString(),killCourse);

                //5 修改发布信息
                killCourse.setPublishStatus(1);
                killCourseMapper.updateById(killCourse);
            }
            baseMapper.updateById(killActivity.setPublishStatus(1));
            redisTemplate.opsForHash().putAll(cons.KILL_COURSE+killActivity.getId(),hashMap);

        }

        return true;
    }

    /**
     * 用户秒杀课程
     * @param killDto
     * @return
     * @throws Exception
     */
    @Override
    public JsonResult killCourse(KillDto killDto) {
        //1 校验验证码
        String code = killDto.getCode();
        AssertUtil.isTrue(code!=null,"验证码为空");
        KillCourse  o = (KillCourse) redisTemplate.opsForHash().get(cons.KILL_COURSE + killDto.getKillActivityId(), killDto.getCourseId().toString());
        AssertUtil.isTrue(code.equals(o.getCode()),"验证码错误");

        //2 校验用户是否已购买过
        Object user = redisTemplate.opsForValue().get(cons.KILL_COURSE_USER + o.getCourseId() + ":" + killDto.getLoginId());
        AssertUtil.isTrue(user==null,"不能重复购买");

        //3 校验是否购买超单
        AssertUtil.isTrue(killDto.getCount()<=o.getKillLimit(),"超过购买限制数量");

        //4 扣减库存
        RSemaphore semaphore = redissonClient.getSemaphore(cons.KILL_STORE + killDto.getCourseId());
        boolean flag = semaphore.tryAcquire(Math.toIntExact(killDto.getCount()));
        AssertUtil.isTrue(flag,"库存不足");

        //5 生成订单信息
        String productCode = CodeGenerateUtils.generateProductCode();
        KillOrderDto killOrderDto = new KillOrderDto()
                .setOrderNo(productCode)
                .setPayAmount(o.getKillPrice())
                .setTotalCount(Math.toIntExact(killDto.getCount()))
                .setUserId(killDto.getLoginId())
                .setAmount(o.getKillPrice())
                .setCount(Math.toIntExact(killDto.getCount()))
                .setCourseId(killDto.getCourseId())
                .setCourseName(o.getCourseName())
                .setCoursePic(o.getCoursePic())
                .setKillCourseId(o.getId());

        //6 发送延迟消息检查用户是否下单
        HashMap<String,String> hashMap= new HashMap<>();
        hashMap.put("orderNo",productCode);
        hashMap.put("courseId",killDto.getCourseId().toString());
        SendResult sendResult = rocketMQTemplate.syncSend(
                cons.KILL_ORDER_TOPIC + ":" + cons.KILL_ORDER_TARGET,
                MessageBuilder.withPayload(JSON.toJSONString(hashMap)).build(),
                3000,
                5
        );
        AssertUtil.isTrue(sendResult.getSendStatus()== SendStatus.SEND_OK,"事务消息发送失败");

        //6 保存订单信息到redis
        redisTemplate.opsForValue().set(productCode, JSON.toJSONString(killOrderDto));

        //6 保存用户信息到redis
        redisTemplate.opsForValue().set(cons.KILL_COURSE_USER+o.getCourseId()+":"+killDto.getLoginId(),productCode);

        return JsonResult.me(productCode);
    }
}
